#include "linux/of.h"
#include "linux/io.h"
#include "linux/compiler.h"
#include "linux/init.h"
#include "linux/string.h"
#include "linux/ioport.h"
#include "linux/of_fdt.h"
#include "linux/errno.h"
#include "linux/of_address.h"
#include "linux/of_platform.h"
#include "linux/printk.h"

#define OF_MAX_ADDR_CELLS 4
#define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS)
#define OF_CHECK_COUNTS(na, ns) (OF_CHECK_ADDR_COUNT(na) && (ns) > 0)

struct of_bus
{
    char *name; // 总线名称（如 "pci", "default"）
    char *addresses; // 地址属性名（如 "reg", "assigned-addresses"）
    int (*match)(struct device_node *parent); // 总线匹配函数
    void (*count_cells)(struct device_node *child, int *addrc, int *sizec); // 获取地址/大小单元数
    uint64_t (*map)(uint32_t *addr, uint32_t *range, int na, int ns, int pna); // 地址映射函数
    int (*translate)(uint32_t *addr, uint64_t offset, int na); // 地址平移函数
    unsigned int (*get_flags)(uint32_t *addr); // 获取资源标志（如 IORESOURCE_MEM）
};

/*
 * 以十六进制格式打印地址数组（调试用）
 * s:    输出前缀字符串
 * addr: 地址数组指针（大端字节序）
 * na:   地址单元数量（如 #address-cells 的值）
 */
static void of_dump_addr(char *s, uint32_t *addr, int na)
{
    printk("%s", s);
    while (na--)
        printk(" %08x", be32_to_cpu(*(addr++)));
    printk("\n");
}

/*
 * 默认地址映射，计算子地址到父地址的偏移量
 * addr:  子地址数组（大端）
 * range: ranges 属性中的条目（子地址+父地址+大小）
 * na:    子地址单元数
 * ns:    大小单元数
 * pna:   父地址单元数
 * 返回值：成功返回偏移量，失败返回 OF_BAD_ADDR
 */
static uint64_t of_bus_default_map(uint32_t *addr, uint32_t *range, int na, int ns, int pna)
{
    uint64_t cp, s, da;

    cp = of_read_number(range, na); // 解析父总线基地址（range 的前 na 个单元）
    s = of_read_number(range + na + pna, ns); // 解析地址范围大小（range 的后 ns 个单元）
    da = of_read_number(addr, na); // 解析子总线地址（addr 的前 na 个单元）

    printk("default map, cp=%lx, s=%lx, da=%lx\n",
           (unsigned long)cp, (unsigned long )s, (unsigned long)da);

    // 检查子地址是否在父地址范围内
    if (da < cp || da >= (cp + s))
        return OF_BAD_ADDR;
    return da - cp;
}

/*
 * 将偏移量叠加到地址上，生成父总线地址
 * addr:   地址数组（输入为子地址，输出为父地址）
 * offset: 偏移量
 * na:     父地址单元数
 * 返回值：固定返回 0（成功）
 */
static int of_bus_default_translate(uint32_t *addr, uint64_t offset, int na)
{
    uint64_t a = of_read_number(addr, na); // 读取当前地址并转换为 CPU 格式
    memset(addr, 0, na * 4);
    a += offset;  // 应用偏移量
    if (na > 1)
        addr[na - 2] = cpu_to_be32(a >> 32); // 高位部分（64 位时有效）
    addr[na - 1] = cpu_to_be32(a & 0xffffffffu); // 低位部分

    return 0;
}


/*
 * 将设备树地址转换为内核资源结构体（struct resource）
 * dev:    设备节点
 * addrp:  设备树中的地址数组（大端）
 * size:   地址范围大小
 * flags:  资源标志（如 IORESOURCE_MEM）
 * name:   资源名称（可选）
 * r:      输出的资源结构体指针
 * 返回值：成功返回 0，失败返回错误码
 */
static int __of_address_to_resource(struct device_node *dev,
                                    uint32_t *addrp, uint64_t size, unsigned int flags,
                                    char *name, struct resource *r)
{
    uint64_t taddr;

    if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)  // 检查标志合法性
        return -EINVAL;

    taddr = of_translate_address(dev, addrp);  // 转换为物理地址
    if (taddr == OF_BAD_ADDR)
        return -EINVAL;

    memset(r, 0, sizeof(struct resource));
    if (flags & IORESOURCE_IO)   // I/O 资源（未实现）
    {
        // unsigned long port;
        // port = pci_address_to_pio(taddr);
        // if (port == (unsigned long)-1)
        // return -EINVAL;
        // r->start = port;
        // r->end = port + size - 1;
    }
    else  // 内存资源
    {
        r->start = taddr;   // 起始地址
        r->end = taddr + size - 1; // 结束地址
    }
    r->flags = flags; // 设置标志（如 IORESOURCE_MEM）
    r->name = name ? name : dev->full_name; // 资源名称（默认用节点全名）

    return 0;
}

static unsigned int of_bus_default_get_flags(uint32_t *addr)
{
    return IORESOURCE_MEM;
}

/*
 * 获取设备节点的地址和大小单元数
 * dev:    设备节点
 * addrc:  输出地址单元数（#address-cells）
 * sizec:  输出大小单元数（#size-cells）
 */
static void of_bus_default_count_cells(struct device_node *dev, int *addrc, int *sizec)
{
    if (addrc)
        *addrc = of_n_addr_cells(dev); // 获取 #address-cells
    if (sizec)
        *sizec = of_n_size_cells(dev); // 获取 #size-cells
}

static struct of_bus of_busses[] = {
#ifdef CONFIG_OF_ADDRESS_PCI
    /* PCI */
    {
        .name = "pci",
        .addresses = "assigned-addresses",
        .match = of_bus_pci_match,
        .count_cells = of_bus_pci_count_cells,
        .map = of_bus_pci_map,
        .translate = of_bus_pci_translate,
        .get_flags = of_bus_pci_get_flags,
    },
#endif /* CONFIG_OF_ADDRESS_PCI */
#if 0
	/* ISA */
	{
		.name = "isa",
		.addresses = "reg",
		.match = of_bus_isa_match,
		.count_cells = of_bus_isa_count_cells,
		.map = of_bus_isa_map,
		.translate = of_bus_isa_translate,
		.get_flags = of_bus_isa_get_flags,
	},
#endif
    /* Default */
    {
        .name = "default",
        .addresses = "reg",
        .match = NULL,
        .count_cells = of_bus_default_count_cells,
        .map = of_bus_default_map,
        .translate = of_bus_default_translate,
        .get_flags = of_bus_default_get_flags,
    },
};

/*
 * 根据父节点选择对应的总线处理类型
 * np: 父设备节点
 * 返回值：匹配的 of_bus 结构体指针
 */
static struct of_bus *of_match_bus(struct device_node *np)
{
    int i;

    // 遍历所有总线类型
    for (i = 0; i < (sizeof(of_busses) / sizeof(struct of_bus)); i++)
        if (!of_busses[i].match || of_busses[i].match(np))
            return &of_busses[i]; // 返回第一个匹配的总线

    // BUG();
    return NULL;
}

/*
 * 从设备树节点的 reg 属性中提取地址信息
 * dev:    设备节点
 * index:  地址条目的索引
 * size:   输出地址范围大小
 * flags:  输出资源标志
 * 返回值：地址数组指针（大端），失败返回 NULL
 */
uint32_t *of_get_address(struct device_node *dev, int index, uint64_t *size, unsigned int *flags)
{
    uint32_t *prop;
    uint32_t psize;
    struct device_node *parent;
    int onesize, i, na, ns;

    /* Get parent & match bus type */
    parent = of_get_parent(dev);  // 获取父节点
    if (parent == NULL)
        return NULL;

    of_bus_default_count_cells(dev, &na, &ns); // 获取子节点单元数
    of_node_put(parent);  // 减少父节点引用计数
    if (!OF_CHECK_ADDR_COUNT(na)) // 检查地址单元合法性
        return NULL;

    /* Get "reg" or "assigned-addresses" property */
    prop = of_get_property(dev, "reg", &psize); // 获取 reg 属性值
    if (prop == NULL)
        return NULL;
    psize /= 4;  // 转换为 32 位单元数

    onesize = na + ns; // 单个地址条目长度（地址+大小）
    for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
        if (i == index) // 找到指定索引的条目
        {
            if (size)
                *size = of_read_number(prop + na, ns); // 解析大小
            if (flags)
                *flags = of_bus_default_get_flags(prop); // 获取资源标志
            return prop; // 返回地址数组（大端）
        }
    return NULL;
}

int of_address_to_resource(struct device_node *dev, int index, struct resource *r)
{
    uint32_t *addrp;
    uint64_t size;
    unsigned int flags;
    char *name = NULL;

    addrp = of_get_address(dev, index, &size, &flags);
    if (addrp == NULL)
        return -1;

    /* Get optional "reg-names" property to add a name to a resource */
    of_property_read_string_index(dev, "reg-names", index, &name);

    return __of_address_to_resource(dev, addrp, size, flags, name, r);
}

int of_empty_ranges_quirk(struct device_node *np)
{
    return 0;
}

/*
 * 将当前总线地址转换为父总线地址（单级转换）
 * parent: 父设备节点
 * bus:    当前总线类型
 * pbus:   父总线类型
 * addr:   地址数组（输入为当前地址，输出为父地址）
 * na:     当前地址单元数
 * ns:     大小单元数
 * pna:    父地址单元数
 * rprop:  ranges 属性名（如 "ranges"）
 * 返回值：成功返回 0，失败返回 1
 */
static int of_translate_one(struct device_node *parent, struct of_bus *bus,
                            struct of_bus *pbus, uint32_t *addr,
                            int na, int ns, int pna, char *rprop)
{
    uint32_t *ranges;
    uint32_t rlen;
    int rone;
    uint64_t offset = OF_BAD_ADDR;

    /*
     * Normally, an absence of a "ranges" property means we are
     * crossing a non-translatable boundary, and thus the addresses
     * below the current cannot be converted to CPU physical ones.
     * Unfortunately, while this is very clear in the spec, it's not
     * what Apple understood, and they do have things like /uni-n or
     * /ht nodes with no "ranges" property and a lot of perfectly
     * useable mapped devices below them. Thus we treat the absence of
     * "ranges" as equivalent to an empty "ranges" property which means
     * a 1:1 translation at that level. It's up to the caller not to try
     * to translate addresses that aren't supposed to be translated in
     * the first place. --BenH.
     *
     * As far as we know, this damage only exists on Apple machines, so
     * This code is only enabled on powerpc. --gcl
     */
    ranges = of_get_property(parent, rprop, &rlen); // 获取 ranges 属性
    if (ranges == NULL && !of_empty_ranges_quirk(parent))
    {
        printk("no ranges; cannot translate\n");
        return 1;
    }

     // 处理空 ranges（1:1 映射）
    if (ranges == NULL || rlen == 0)
    {
        offset = of_read_number(addr, na); // 直接读取地址
        memset(addr, 0, pna * 4);
        printk("empty ranges; 1:1 translation\n");
        goto finish;
    }

    printk("walking ranges...\n");

    /* Now walk through the ranges */
    rlen /= 4;   // 转换为 32 位单元数
    rone = na + pna + ns;  // 单个 ranges 条目长度
    // 遍历 ranges 条目查找匹配项
    for (; rlen >= rone; rlen -= rone, ranges += rone)
    {
        offset = bus->map(addr, ranges, na, ns, pna); // 计算偏移量
        if (offset != OF_BAD_ADDR)  // 找到有效偏移
            break;
    }

    if (offset == OF_BAD_ADDR)
    {
        printk("not found !\n");
        return 1;
    }
    memcpy(addr, ranges + na, 4 * pna);  // 复制父总线地址部分

finish:
    of_dump_addr("parent translation for:", addr, pna);
    printk("with offset: %lx\n", (unsigned long)offset);

    /* Translate it into parent bus space */
    return pbus->translate(addr, offset, pna); // 应用父总线地址转换
}

/*
 * 功能：递归将设备树地址转换为物理地址
 * dev:     起始设备节点
 * in_addr: 输入的地址数组（大端）
 * rprop:   ranges 属性名（如 "ranges"）
 * 返回值：转换后的物理地址（失败返回 OF_BAD_ADDR）
 */
static uint64_t __of_translate_address(struct device_node *dev,
                                       uint32_t *in_addr, char *rprop)
{
    struct device_node *parent = NULL;
    struct of_bus *bus, *pbus;
    uint32_t addr[OF_MAX_ADDR_CELLS]; // 存储当前地址
    int na, ns, pna, pns;  // 单元数
    uint64_t result = OF_BAD_ADDR;

    printk("** translation for device %s **\n", of_node_full_name(dev));

    /* Increase refcount at current level */
    of_node_get(dev);

    /* Get parent & match bus type */
    parent = of_get_parent(dev);  // 获取父节点
    if (parent == NULL)
        goto bail;
    bus = of_match_bus(parent);  // 匹配当前总线类型

    /* Count address cells & copy address locally */
    of_bus_default_count_cells(dev, &na, &ns);  // 获取子节点单元数
    if (!OF_CHECK_COUNTS(na, ns))
    {
        printk("Bad cell count for %s\n", of_node_full_name(dev));
        goto bail;
    }
    memcpy(addr, in_addr, na * 4);

    printk("bus is %s (na=%d, ns=%d) on %s\n",
           bus->name, na, ns, of_node_full_name(parent));
    of_dump_addr("translating address:", addr, na);

    /* Translate */
    for (;;) 
    {
        /* Switch to parent bus */
        of_node_put(dev); // 释放当前设备节点引用
        dev = parent; // 更新当前设备节点
        parent = of_get_parent(dev);

        /* If root, we have finished */
        if (parent == NULL) // 到达根节点
        {
            printk("reached root node\n");
            result = of_read_number(addr, na);  // 直接读取地址
            break;
        }

        /* Get new parent bus and counts */
        pbus = of_match_bus(parent); // 父总线类型
        of_bus_default_count_cells(dev, &pna, &pns); // 获取父节点单元数
        if (!OF_CHECK_COUNTS(pna, pns))   
        {
            printk("Bad cell count for %s\n",
                   of_node_full_name(dev));
            break;
        }

        printk("parent bus is %s (na=%d, ns=%d) on %s\n",
               pbus->name, pna, pns, of_node_full_name(parent));

        /* Apply bus translation */
        if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop))
            break;

        /* Complete the move up one level */
        na = pna;  // 更新为父单元数
        ns = pns;
        bus = pbus; // 更新为父总线

        of_dump_addr("one level translation:", addr, na);
    }
bail:
    of_node_put(parent);
    of_node_put(dev);

    return result;
}


/*
* 为什么需要 of_translate_address 进行地址转换
* 因为设备树地址的“物理性”是相对的
* 设备树中的地址（如 reg 属性）描述的是 设备视角的物理地址，但这个地址可能属于某个总线或桥接器的地址空间，而非 CPU 的全局物理地址空间。例如：

* PCI 设备：reg 中的地址可能是 PCI 总线地址，需要通过 PCI 桥的地址映射规则转换为 CPU 物理地址。
* 多级总线：设备可能挂接在多个总线层级下（如 SoC内部总线 -> PCI桥 -> PCI设备），每级总线可能定义自己的地址空间。
* 此时，设备树中的地址是 相对于当前总线层级 的物理地址，需要通过 ranges 属性逐级转换到 CPU 的物理地址空间。

* ranges 属性的作用
* 在设备树中，父节点通过 ranges 属性定义 子总线地址空间到父总线地址空间的映射关系。例如：

* <DTS>
* soc {
*   #address-cells = <1>;
*   #size-cells = <1>;
*   ranges = <0x0 0xfe000000 0x1000000>; // 子地址 0x0 映射到父地址 0xfe000000，范围 0x1000000

*   pci@1f000000 {
*     #address-cells = <3>;
*     #size-cells = <2>;
*     ranges = <0x02000000 0 0x80000000 0x80000000 0 0x20000000>; // PCI 地址到 CPU 地址的映射
*   };
* };
* 子设备地址：设备树中的 reg 地址是相对于其父总线的。
* 转换需求：需要根据 ranges 将子总线地址逐级转换到父总线地址，最终得到 CPU 物理地址。
*
  of_translate_address 的核心逻辑
* of_translate_address 函数的作用是 递归遍历设备树层级，逐级应用总线地址转换规则。其过程如下：

* 从设备节点出发：获取设备的 reg 地址（相对于父总线）。
* 递归向上查找父节点：直到根节点（全局物理地址空间）。
* 逐级应用 ranges：
* 每级总线可能定义自己的地址映射规则（通过 ranges 属性）。
* 调用总线类型（struct of_bus）的 map 和 translate 方法完成地址转换。
* 示例场景
* 假设设备树结构如下：

* <DTS>
* // Root Node (CPU Physical Address Space)
* soc {
*   ranges = <0x0 0xfe000000 0x1000000>; // 子地址 0x0 → 父地址 0xfe000000

*   pci@1f000000 {
*       ranges = <0x02000000 0 0x80000000 0x80000000 0 0x20000000>; // PCI 地址 → CPU 地址
*       device@0 {
*           reg = <0x0 0x0 0x1000>; // 设备在 PCI 总线上的地址
*       };
*   };
* };
* 转换步骤：
* 设备 device@0 的 reg 地址 0x0 是相对于 PCI 总线的。
* 通过 PCI 总线的 ranges，将 0x0 转换为 CPU 地址 0x80000000。
* 再通过 soc 的 ranges，将 0x80000000 映射为最终的 CPU 物理地址 0xfe000000 + 0x80000000。
* 
* 特殊总线类型的差异化处理
* 不同总线类型（如 PCI、ISA、默认内存总线）的地址转换规则不同。of_translate_address 通过 总线抽象层（struct of_bus） 支持差异化处理：

* PCI 总线：处理复杂的 BAR（Base Address Register）映射。
* 默认内存总线：直接 1:1 映射或偏移转换。
* ISA 总线：处理 I/O 端口和内存地址的转换。
* 
* 地址转换的必要性
* 统一物理地址空间：CPU 需要所有设备的地址最终映射到统一的物理地址空间。
* 总线隔离：不同总线可能有独立的地址空间（如 PCI 总线地址与 CPU 物理地址不同）。
* 硬件兼容性：支持地址重映射、动态配置（如 PCI BAR）。
*/
uint64_t of_translate_address(struct device_node *dev, uint32_t *in_addr)
{
    return __of_translate_address(dev, in_addr, "ranges");
}

void __iomem *of_iomap(struct device_node *np, int index)
{
	struct resource res;

	if (of_address_to_resource(np, index, &res))
		return NULL;

	return ioremap(res.start, resource_size(&res));
    //return (void *)(res.start);
}